package org.haptimap.hcimodules.util;

import java.util.Comparator;

import android.location.Location;

/**
 * Class representing a very simple destination. If the destination
 * does not have a valid name, the name will remain as "No name" 
 * 
 * @author Miguel Molina
 */
public class WayPoint implements Comparator<WayPoint>, Comparable<WayPoint>{
	private static final String EMPTY = "No name";
	public static final String PROVIDER = "HaptiMap";
	protected Location wayPointLocation = null;
	private String wayPointName = EMPTY;

	/**
	 * Constructs a new WayPoint. If the name is empty,
	 * the default name will be set to <strong>"No Name"</strong>.
	 * 
	 * @param destinationName The name of the destination point
	 * @param latitude The latitude in WGS84 format
	 * @param longitude The longitude in WGS84 format
	 * 
	 * @throws IllegalArgumentException if latitude or longitude string-to-number 
	 * values are invalid.
	 */
	public WayPoint(String destinationName, String latitude, String longitude){
		try{
			validateValues(new Double(latitude)*1E6, new Double(longitude)*1E6);
		} catch (NumberFormatException exception){
			throw new IllegalArgumentException("latitude=" + latitude + " or longitude=" + longitude + " contains an invalid value");
		}
		setName(destinationName);
	}

	/**
	 * Constructs a new WayPoint. If the name is empty,
	 * the default name will be set to <strong>"No Name"</strong>.
	 * 
	 * @param destinationName The name of the destination point. 
	 * @param destination The destination {@link Location} 
	 */
	public WayPoint(String destinationName, Location destination){
		if(destination == null){
			throw new NullPointerException("destination");
		}
		wayPointLocation = destination;
		setName(destinationName);
	}

	/**
	 * Constructs a new WayPoint. If the name is empty,
	 * the default name will be set to <strong>"No Name"</strong>.
	 * 
	 * @param destinationName The name of the destination point. 
	 * @param latlon An array containing the latitude and the longitude as follows:
	 * <p> 
	 * <ul>
	 * <li> <strong>latlon[0]:</strong> The latitude in WGS84 format.
	 * <li> <strong>latlon[1]:</strong> The longitude in WGS84 format.
	 * </ul>   
	 * </p>
	 * @throws IllegalArgumentException if latlon is null or has length < 2
	 */
	public WayPoint(String destinationName, double[] latlon){
		if (latlon == null || latlon.length < 2) {
			throw new IllegalArgumentException("latlon is null or has length < 2");
		}
		validateValues(new Double(latlon[0])*1E6, new Double(latlon[1])*1E6);
		setName(destinationName);
	}

	// Validates the current values and throws an exception if they are NaN
	private boolean validateValues(Double lat, Double lon){
		boolean ans = false;
		if(lat.isNaN() || lon.isNaN()){
			ans = false;
			throw new NumberFormatException("latitude or longiture is NaN");
		} else {
			wayPointLocation = new Location(PROVIDER);
			wayPointLocation.setLatitude(lat.intValue()/1E6);
			wayPointLocation.setLongitude(lon.intValue()/1E6);
			ans = true;
		}
		return ans;
	}

	/**
	 * The {@link Location} object to this waypoint
	 * @return the {@link Location} for this waypoint
	 */
	public Location getLocation(){
		return new Location(wayPointLocation);
	}

	// Validates the destination name
	private void setName(String destinationName){
		if(destinationName.length() > 1){
			wayPointName = destinationName;
		} else {
			wayPointName = EMPTY;
		}
	}

	/**
	 * The name of this waypoint. If the name was invalid when crating
	 * setting the name, the current value will be: {@value #EMPTY}
	 * 
	 * @return The name for this waypoint
	 */
	public String getName(){
		return new String(wayPointName);
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime
				* result
				+ ((wayPointLocation == null) ? 0 : new Double(wayPointLocation.getLatitude()).hashCode() >> 13);
		result = prime
				* result
				+ ((wayPointLocation == null) ? 0 : new Double(wayPointLocation.getLongitude()).hashCode() >> 13);
		result = prime * result
				+ ((wayPointName == null) ? 0 : wayPointName.hashCode());		
		return result;
	}

	public int compareTo(WayPoint arg0) {
		return compare(WayPoint.this, arg0);
	}

	@Override
	public boolean equals(Object obj) {
		if(obj == null){
			return false;
		}
		return compareTo((WayPoint)obj) == 0;
	}

	public int compare(WayPoint arg0, WayPoint arg1) {
		double lat1, lat2, lon1, lon2;

		lat1 = arg0.wayPointLocation.getLatitude();
		lat2 = arg1.wayPointLocation.getLatitude();
		lon1 = arg0.wayPointLocation.getLongitude();
		lon2 = arg1.wayPointLocation.getLongitude();

		int ans = Double.compare(lat1, lat2);

		if(ans == 0)	{
			return Double.compare(lon1, lon2);
		} else {
			return  ans; 
		}
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("WayPoint [" +
				"Name=" + wayPointName +
				" latitude=" + wayPointLocation.getLatitude() +
				" longitude=" + wayPointLocation.getLongitude() +
				"]");
		return builder.toString();
	}

}


